#include "InstanceBacklog.h"

InstanceBacklog::InstanceBacklog()
:Instance() {
	capacityCost = NULL;
	upperBound = NULL;
	setupCost = NULL;
	backlogCost = NULL;
	totalCapacity = NULL;
	demand = NULL;
	holdingCost = NULL;
	resourceCost = NULL;
	setupTime = NULL;
	successor = NULL;
	successorCount = NULL;
	endProducts = NULL;
}

InstanceBacklog::~InstanceBacklog() {
	if (capacityCost != NULL)
		delete [] capacityCost;

	if (upperBound != NULL)
		delete [] upperBound;

	if (setupCost != NULL)
		delete [] setupCost;

	if (backlogCost != NULL)
		delete [] backlogCost;

	if (totalCapacity != NULL)
		delete [] totalCapacity;

	if (demand != NULL)
		delete [] demand;

	if (holdingCost != NULL)
		delete [] holdingCost;

	if (resourceCost != NULL)
		delete [] resourceCost;

	if (setupTime != NULL)
		delete [] setupTime;

	if (endProducts != NULL)
		delete [] endProducts;

	if (successorCount != NULL)
		delete [] successorCount;

	if (successor != NULL) {
		for (int j = 0; j < J; j++)
			if (successor[j] != NULL)
				delete [] successor[j];

		delete [] successor;
	}
}

bool InstanceBacklog::load( const char *name ) {

	// Initializing name
	if (name == NULL) {
		cout << "ERROR: unnamed instance" << endl;
		return false;
	}

	int size = strlen(name);
	this->name = new char[size + 1];
	strcpy( this->name, name );

	// Opening file...
	bool set = (name[0] == 'S');
	char *path = getPath(name);
	Input in( path );

	if (in.fail()) {
		cout << "ERROR: could not open instance file: " << path << endl;
		return false;
	}

	delete [] path;

	// File is open. Declare variables...
	int je = 0;
	int *tmp;

	// SET insance
	if (set) {
		flagFinalBacklog = true;

		int F;
		double *transp;
		double *familySetupTime;
		double *family;
		double bc;

		// Reading data...
		in >> J;
		in >> Je;
		in >> T;
		in >> M;
		in >> F;

		// Reading backlog factor...
		in >> bc;

		// Reading holding cost...
		holdingCost = new double[J];
		in.readArray( holdingCost, J );

		// Calculating backlog cost...
		backlogCost = new double[J];
		for (int j = 0; j < J; j++)
			backlogCost[j] = bc * holdingCost[j];

		// Reading demand...
		demand = new double[J*T];
		in.readArray( demand, J*T );

		// Reading capacity cost...
		transp = new double[M*J];
		in.readArray( transp, M*J );

		capacityCost = new double[M*J];
		for (int j = 0; j < J; j++)
			for (int m = 0; m < M; m++)
				capacityCost[ m*J + j ] = transp[ j*M + m ];
		
		delete [] transp;

		// Reading capacity...
		totalCapacity = new double[M*T];
		in.readArray( totalCapacity, M*T );

		// Reading setup time...
		familySetupTime = new double[M*F];
		in.readArray( familySetupTime, M*F );

		// Reading resource cost...
		resourceCost = new double[J*J];
		in.readArray( resourceCost, J*J );

		tmp = new int[J];
		successor = new int*[J];
		successorCount = new int[J];
		endProducts = new int[Je];
		je = 0;

		for (int j = 0; j < J; j++) {
			successorCount[j] = 0;

			for (int k = 0; k < J; k++) {
				if (resourceCost[ j*J + k ] > 0)
					tmp[ successorCount[j]++ ] = k;
			}

			if (successorCount[j] > 0) {
				successor[j] = new int[successorCount[j]];
				for (int k = 0; k < successorCount[j]; k++)
					successor[j][k] = tmp[k];
			}
			else {
				successor[j] = NULL;
				endProducts[je++] = j;
			}
		}

		delete [] tmp;

		// Reading families
		// Here we adjust each product setup time

		family = new double[J*F];
		in.readArray( family, J*F );

		setupTime = new double[M*J];
		for (int m = 0; m < M; m++)
			for (int j = 0; j < J; j++) 
				for (int f = 0; f < F; f++) 
					if (family[j*F + f] > 0) {
						setupTime[m*J + j] = familySetupTime[f*M + m];
						break;
					}
					else
						setupTime[m*J + j] = 0;

		delete [] familySetupTime;

		// Creating setup cost
		// PS: these instances have no setup cost
		setupCost = new double[J];
		for (int j = 0; j < J; j++)
			setupCost[j] = 0.0;
	}

	// ABCD instance
	else {
		flagFinalBacklog = false;

		// Reading data...
		in >> J;
		in >> T;
		in >> M;

		// Reading setup cost...
		setupCost = new double[J];
		in.readArray( setupCost, J );

		// Reading holding cost...
		holdingCost = new double[J];
		in.readArray( holdingCost, J );

		// Calculating backlog cost...
		backlogCost = new double[J];
		for (int j = 0; j < J; j++)
			backlogCost[j] = 10*holdingCost[j];

		// Reading overtime cost, I mean, wasting it...
		double waste;
		for (int m = 0; m < M; m++)
			for (int t = 0; t < T; t++)
				in >> waste;

		// Reading demand...
		// PS: 20% increased demand for first half of time horizon
		demand = new double[J*T];
		in.readArray( demand, J*T );

		for (int j = 0; j < J; j++)
			for (int t = 0; t < T/2; t++)
				demand[j*T+t] = 1.2 * demand[j*T+t];

		// Reading resource cost...
		resourceCost = new double[J*J];
		in.readArray( resourceCost, J*J );

		tmp = new int[J];
		successor = new int*[J];
		successorCount = new int[J];
		endProducts = new int[J];
		je = 0;

		for (int j = 0; j < J; j++) {
			successorCount[j] = 0;

			for (int k = 0; k < J; k++)
				if (resourceCost[ j*J + k ] > 0)
					tmp[ successorCount[j]++ ] = k;

			if (successorCount[j] > 0) {
				successor[j] = new int[successorCount[j]];
				for (int k = 0; k < successorCount[j]; k++)
					successor[j][k] = tmp[k];
			}
			else {
				successor[j] = NULL;
				endProducts[je++] = j;
			}
		}

		Je = je;

		delete [] tmp;

		// Reading capacity cost...
		capacityCost = new double[M*J];
		in.readArray( capacityCost, M*J );

		// Reading setup time...
		setupTime = new double[M*J];
		in.readArray( setupTime, M*J );

		// Reading capacity...
		// PS: 10% increased capacity
		totalCapacity = new double[M*T];
		in.readArray( totalCapacity, M*T );

		for (int m = 0; m < M; m++)
			for (int t = 0; t < T; t++)
				totalCapacity[m*T+t] = 1.1 * totalCapacity[m*T+t];
	}

	// Calculating upper bounds.
	upperBound = new double[J*T];
	double value;
	int index;

	for (int j = 0; j < J; j++)
		for (int t = 0; t < T; t++) {
			index = j*T + t;
			upperBound[ index ] = calcTotalDemand(j, t);
			
			for (int m = 0; m < M; m++) {
				value = (getTotalCapacity(m, t) - getSetupTime(m, j))/getCapacityCost(m, j);
				if (value < upperBound[index])
					upperBound[index] = value;
			}
		}

	print();
	ready = true;
	return true;
}

double InstanceBacklog::calcTotalDemand( int j, int t ) {
	double dk = 0;
	int k;

	for (int p = t; p < T; p++)
		dk += getDemand(j, p);

	for (int i = 0; i < successorCount[j]; i++) {
		k = successor[j][i];
		dk += getResourceCost(j, k) * calcTotalDemand(k, t);
	}

	return dk;
}

char* InstanceBacklog::getPath( const char *name ) {
	char *path;
	char type = name[0] + ('a' - 'A');

	if (type == 's') {
		path = new char[ strlen(name) + strlen(DIR) + strlen(EXT) + 7];
		sprintf(path, "%s/set/%s%s", DIR, name, EXT);
	}
	else {
		path = new char[ strlen(name) + strlen(DIR) + strlen(EXT) + 13];
		sprintf(path, "%s/overtime/%c/%s%s", DIR, type, name, EXT);
	}
	return path;
}

int InstanceBacklog::getSolutionSize() {
	return J*T;
}

double InstanceBacklog::getCapacityCost( int m, int j ) {
	if (capacityCost != NULL)
		return capacityCost[ m*J + j ];
	return -1;
}

double InstanceBacklog::getUpperBound( int j, int t ) {
	if (upperBound != NULL)
		return upperBound[ j*T + t ];
	return -1;
}

double InstanceBacklog::getSetupCost( int j ) {
	if (setupCost != NULL)
		return setupCost[ j ];
	return -1;
}

double InstanceBacklog::getBacklogCost( int j ) {
	if (backlogCost != NULL)
		return backlogCost[ j ];
	return -1;
}

double InstanceBacklog::getTotalCapacity( int m, int t ) {
	if (totalCapacity != NULL)
		return totalCapacity[ m*T + t ];
	return -1;
}

double InstanceBacklog::getDemand( int j, int t ) {
	if (demand != NULL)
		return demand[ j*T + t ];
	return -1;
}

double InstanceBacklog::getHoldingCost( int j ) {
	if (holdingCost != NULL)
		return holdingCost[j];
	return -1;
}

double InstanceBacklog::getResourceCost( int j, int k ) {
	if (resourceCost != NULL)
		return resourceCost[ j*J + k ];
	return -1;
}

double InstanceBacklog::getSetupTime( int m, int j ) {
	if (setupTime != NULL)
		return setupTime[ m*J + j ];
	return -1;
}

int InstanceBacklog::getSuccessor( int j, int i ) {
	if (successor != NULL && i < successorCount[j])
		return successor[j][i];
	return -1;
}

int InstanceBacklog::getEndProduct( int i ) {
	if (endProducts!= NULL && i < Je)
		return endProducts[i];
	return -1;
}

int InstanceBacklog::getSuccessorCount( int j ) {
	if (j < J)
		return successorCount[j];

	return -1;
}

bool InstanceBacklog::isEndProduct( int j ) {
	for (int i = 0; i < Je; i++)
		if (endProducts[i] == j)
			return true;

	return false;
}

bool InstanceBacklog::allowFinalBacklog() {
	return flagFinalBacklog;
}

void InstanceBacklog::print() {
	ofstream out("Instancia.txt");

	out << "Products: " << J << endl <<
		"Periods: " << T << endl <<
		"Machines: " << M << endl << endl;

	out << "Demand D[j,t]" << endl;
	for (int j = 0; j < J; j++) {
		for (int t = 0; t < T; t++)
			out << getDemand(j,t) << ' ';
		out << endl;
	}
	out << endl;

	out << "Holding Cost h[j]" << endl;
	for (int j = 0; j < J; j++)
		out << getHoldingCost(j) << ' ';
	out << endl << endl;

	out << "Capacity Cost a[m,j]" << endl;
	for (int m = 0; m < M; m++) {
		for (int j = 0; j < J; j++)
			out << getCapacityCost(m,j) << ' ';
		out << endl;
	}
	out << endl;

	out << "Setup Cost sc[j]" << endl;
	for (int j = 0; j < J; j++)
		out << getSetupCost(j) << ' ';
	out << endl << endl;

	out << "Total Capacity C[m,t]" << endl;
	for (int m = 0; m < M; m++) {
		for (int t = 0; t < T; t++)
			out << getTotalCapacity(m,t) << ' ';
		out << endl;
	}
	out << endl;

	out << "Resource Cost r[j,k]" << endl;
	for (int j = 0; j < J; j++) {
		for (int k = 0; k < J; k++)
			out << getResourceCost(j,k) << ' ';
		out << endl;
	}
	out << endl;

	out << "Setup Time st[m,j]" << endl;
	for (int m = 0; m < M; m++) {
		for (int j = 0; j < J; j++)
			out << getSetupTime(m,j) << ' ';
		out << endl;
	}
	out << endl;

	out << "Backlog Cost bc[j]" << endl;
	for (int j = 0; j < J; j++)
		out << getBacklogCost(j) << ' ';
	out << endl << endl;

	out.close();
}